

#include "jncParse.h"
#include "jncGenComb.h"
#include "jncDefs.h"
#include "jncExpression.h"
#include "jncTypeInfo.h"
#include <algorithm>
#include <boost/mem_fn.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/algorithm.hpp>
#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <assert.h>
#include <string>
#include <bits/stream_iterator.h>

typedef XmlTree::XmlIterator          XmlIterator;
typedef XmlTree::XmlTreeWalker        XmlTreeWalker;
typedef XmlTree::XmlPreOrder          XmlPreOrder;
typedef XmlTree::XmlSiblingNode       XmlSiblingNode;
typedef XmlTree::XmlChildNode         XmlChildNode;


// #define ADDTOCONTEXT(cxt, i, node)		\
//   if(i->ori->getState() == COMPLETE)		\
//     cxt.insert(cxt.end(), JncContext(*i, node))

#define CLEARCOMBNP(combnp)			\
  for(int i = combnp.anp-1; i>=0; i--)		\
    free(combnp.tab[i]);			\
  free(combnp.tab)

#define FLATTENARG(x, y)						\
  x = y;								\
  for(std::vector<CplInfo>::iterator i = x.begin(); i != x.end(); i++)	\
    {									\
      std::vector<CplInfo> argv(i->getArgInfo());			\
      if(argv.size())							\
	std::copy(argv.begin(), argv.end(), (i + 1));			\
    }

#define STREXPR(r, data, x) (BOOST_PP_STRINGIZE(x))
#define REFEXPR(r, data, x) (>x)


class ScanInput {
public:
  ScanInput(const jncTypei& v) : expr(getExpression(v)) {}
  ScanInput(const char* s) : expr(s) {}
  ~ScanInput() {} 
  bool operator()(const Token::iterator& nxt, const Token::iterator& prv, std::vector<Token>& tok);
private:
  const char* expr;
};

bool 
ScanInput::operator()(const Token::iterator& nxt, const Token::iterator& prv, std::vector<Token>& tok)
{
  assert(expr);
  boost::match_results<Token::const_iterator> what;
  std::string pattern(nxt, prv);
  try {
    boost::regex expression(expr, boost::regex::extended | boost::regex::icase);
    boost::regex_search(pattern, what, expression, (boost::match_default | boost::match_continuous));
  }catch(boost::regex_error& e)
    {
      std::cout << __FILE__ << ": " << __LINE__ << std::endl;
      std::cout << "Error at pos " << e.position() << std::endl;
      throw;
    }
  for(int i = 1; i <= (what.size() - 1); i++)
    if(what[i].matched)
      {
	std::string s(what[i].first, what[i].second);
	Token tmp(s);
	tok.insert((tok.begin() + (i-1)), tmp);
      }
  return (tok.size())? true: false;
}

// typedef boost::tokenizer<ScanInput, Token::iterator, Token> Tokenizer;

// class Tokenize {
// public:
//   Tokenize(const jncTypei& v): expr(getExpression(v)) {}
//   Tokenize() : expr(NULL) {}
//   bool operator()(Token::iterator& nxt, Token::iterator& prv, Token& tok);
// private:
//   const char* expr;
// };
// bool 
// Tokenize::operator()(Token::iterator& beg, Token::iterator& end, Token& tok)
// { 
//   assert(expr);
//   ScanInput sc(getExpression(*vparse_expr));
//   Tokenizer tk(beg, end, sc);
//   static std::vector<Token> tokens;
//   if(tokens.size())
//     {
//       beg += (Token(beg, end).find(*tokens.begin()) + tokens.begin()->length());
//       tok = *tokens.begin();
//       tokens.erase(tokens.begin());
//       return true;
//     }
//   else
//     std::copy(tk.begin(), tk.end(), tokens.begin());
//   if(tokens.size())
//     {
//       beg += (Token(beg, end).find(*tokens.begin()) + tokens.begin()->length());
//       tok = *tokens.begin();
//       tokens.erase(tokens.begin());
//       return true;
//     }
//   else
//     {
//       beg = end;
//       return false;
//     }
// }

// RtmInfo lookupPrimitiveTable(jncTypei index, Token tok)
// {
//   RtmInfo tmp(primitiv_table[index]);
//   if(!tmp.getObject())
//     return tmp;
//   XmlContent data;
//   data.getContent(tok);
//   std::vector<RtmInfo> arg;arg.insert(arg.begin(), (RtmInfo)data);
//   tmp.setArg(arg);
//   return tmp;
// }

// void InitJinc()
// {
//   // Initialize Jinc env, for now trigger Registration of primitive Types
//    Token tmp; // 
//    lookupPrimitiveTable(P_JNCVOID, tmp);
// }
// void ExitJinc();


RuntimeObject& foldArg(std::vector<RuntimeObject>& vec)
{
  std::vector<RuntimeObject>::iterator x(vec.begin()), y(vec.end());
  std::vector<CplInfo> tmp(x->oci->getArgInfo());
  if(tmp.size())
    {
      assert((x + 1) < y);
      std::vector<RuntimeObject> arg((x + 1), y);
      foldArg(arg);
      assert((arg.begin() + tmp.size()) < arg.end()); // check argument bound
      arg.erase(arg.begin() + tmp.size(), arg.end());
      std::vector<RtmInfo> rtm_arg(arg.size());
      for(std::vector<RuntimeObject>::iterator i = arg.begin(); i != arg.end(); i++)
	rtm_arg.insert(rtm_arg.end(), *i->ori);
      x->ori->setArg(rtm_arg);
      vec.erase((x + 1), (x + tmp.size()));
      return *x;
    }
  else
    return *x;
}

// RtmInfo 
// JncContext::lookupContextTable(CplInfo ci)
// {
//   Context::iterator x(ptrcontext->find(ci.getId()));
//   if( x == ptrcontext->end())
//     return RtmInfo(NULL, NULL, NULL);
//   return RtmInfo(x->second.getObject(), x->second.getHandler(), x->second.getReturn());
// }

RtmInfo& lookupBinaryTable(CplInfo& qci)
{
  
  // TODO: Duplication with lookupBinaryTable and lookupSymbolTable
  if(qci.getId() == UNSET)
    return *(new RtmInfo());
  FnInfo fn_param(getFnParameters(qci.getId()));
  if(!fn_param)
    return *(new RtmInfo());
  std::vector<CplInfo> argc(qci.getArgInfo());
  std::vector<RtmInfo> argr(argc.size());
  if(argr.size())
    std::transform(argc.begin(), argc.end(), argr.begin(), std::ptr_fun(lookupBinaryTable));
  RtmInfo tmp(qci.getId(), fn_param->obj, fn_param->callbacks->first, fn_param->callbacks->second);
  tmp.setArg(argr);
  return *(new RtmInfo(tmp));
}

// RuntimeObject 
// JncContext::createRuntimeObject(std::vector<Token>::iterator& beg, std::vector<Token>::iterator& end, std::vector<CplInfo>::iterator& ci)
// {
//   RtmInfo tmp;
//   if((tmp = lookupPrimitiveTable(ci->getType(), *beg)).getObject())
//     if((tmp = lookupContextTable(*ci)).getObject())
//       if((tmp = lookupBinaryTable(*ci)).getObject())
// 	assert(0);
//   std::vector<RtmInfo> elemts;
//   std::vector<CplInfo>::iterator itci(ci + 1);
//   for(std::vector<Token>::iterator i = (beg + 1); i != end; i++)
//     {
//       elemts[end - i] = *(createRuntimeObject(i, end, itci).ori);
//       ++itci;
//     }
//   // In case we ot a list of elemts
//   std::vector<RtmInfo> args(elemts.begin(), (elemts.begin() + tmp.getArg().size()));
//   tmp.setArg(args);
//   return RuntimeObject(*ci, tmp);
// }


std::vector<std::string> readTagSignature(XmlTreeWalker* it)
{
  TagNode node_info(it->getNode());
  if(!node_info.nodeName())
    return std::vector<std::string>();
  std::vector<std::pair<std::string, std::string> >attr;
  node_info.attributes(attr);
  std::vector<std::string> tagsign(1 + attr.size());
  tagsign[0] = std::string(node_info.nodeName());
  for(int i = 0;i < attr.size(); i++)
    tagsign[(i+1)] = attr[i].first;
  return tagsign;
}

SigTokens BuildTokens(int n, const SigTokens::iterator& tokens_iterator)
{
  arrgt comb;
  SigTokens gen_tokens;
  std::vector<std::string> pattern(*tokens_iterator);
  if(pattern.size())
    for(int i = n; i > (n-1); i--)
      {
	if(!GenCombnp(&comb, n, i))
	  assert(0);
	std::vector<std::string> tokens(i);
	std::vector<std::string>::iterator tmp(tokens.begin());
	for(int j = (comb.pos-1); j >= 0; j--)
	  {
	    for(int k = (i-1); k >= 0; k--)
	      *(tmp + k) = pattern[comb.tab[j][k]-1];
	    gen_tokens.insert(gen_tokens.end(), tokens);
	  }
	CLEARCOMBNP(comb);
      }
  return gen_tokens;
}

SigTokens genSignatureFromTag(XmlTreeWalker* iter)
{
  // use Tag info to build a master signature pattern
  SigTokens signature_tok;
  signature_tok.insert(signature_tok.begin(), readTagSignature(iter));
  // create more signatures by combining differents tokens from master
  return BuildTokens(signature_tok.begin()->size(), signature_tok.begin());
}


std::string getXmlArgValue(std::pair<std::string, std::string>& p)
{
  return p.second;
}

std::vector<std::string> getNodeAttr(XmlTreeWalker* n) {
  std::vector<std::pair<std::string, std::string> >attrs;
  n->getTagNode().attributes(attrs);
  std::vector<std::string> argv(attrs.size());
  std::transform(attrs.begin(), attrs.end(), argv.begin(), boost::function<std::string (std::pair<std::string, std::string>)>(&getXmlArgValue));
  return std::vector<std::string>(argv);
}

RuntimeObject RuntimeObjectFactory(CplInfo& ci, RtmInfo& ri)
{
  return RuntimeObject(ci, ri);
}

// CplInfo& setSymbolicName(CplInfo& o, SymbolNm nm)
// {
//   o.setSymbolicName(nm);
// }


// CplInfo setFnType(std::list<jncTypei>& t)
// {
//   std::vector<jncTypei> vtypes(t.begin(), t.end()); // Avoid list... 
//   CplInfo* v = new CplInfo[vtypes.size()];
//   for(CplInfo* i = v; i != (v + ARRSZ(v, CplInfo)); i++)
//     (*i).setType(*(vtypes.begin() + (i - v)));
//   boost::shared_ptr<CplInfo> auto_del(v,boost::checked_array_deleter<CplInfo>());
//   std::vector<CplInfo> args(v, (v + ARRSZ(v, CplInfo)));
//   v[0].setArgInfo(args);
//   return v[0];
// }


CplInfo getCompileInfo(const Idnb& i) 
{
  
  CplInfo tmp(i);
  FnInfo fnparam(getFnParameters(tmp));
  tmp.setPriority(fnparam->level);
  tmp.setSymbolicName(*(fnparam->pssignature->begin()));
  tmp.setType(*(fnparam->ptsignature->begin()));
  return tmp;
}

CplInfo& lookupSymbolTable(const std::vector<Token>& x)
{
  std::string sname(*x.begin());
  for(std::vector<Token>::const_iterator i = ++x.begin(); i != x.end(); i++)
    if(i->length())
      sname += CLASSDELM + *i;
  std::vector<std::string> sig(x.begin(), x.end());
  CplInfo tmp(computeId(sig));
  FnInfo fn_param(getFnParameters(tmp));
  if(!fn_param)
    {
      CplInfo not_found;
      not_found.setSymbolicName(sname);
      return *(new CplInfo(not_found));
    }
  tmp = getCompileInfo(tmp.getId());
  tmp.setSymbolicName(sname);
  return *(new CplInfo(tmp));
}

std::list<RuntimeObject> parseXmlElement(XmlTreeWalker* node)
{
  if(!node)
    return std::list<RuntimeObject>();
  SigTokens fnt_signature(genSignatureFromTag(node));
  if(!fnt_signature.size())
    return std::list<RuntimeObject>();
  std::list<CplInfo> cilist;
  for(SigTokens::iterator i = fnt_signature.begin(); i != fnt_signature.end(); i++)
    {
      std::vector<Token> tmp(i->begin(), i->end());
      if(lookupSymbolTable(tmp).getId() != UNSET)
	{
	  cilist.insert(cilist.end(), lookupSymbolTable(tmp));
	  break;
	}
    }
  std::list<RtmInfo> rilist(cilist.size());
  std::transform(cilist.begin(), cilist.end(), rilist.begin(), boost::function<RtmInfo& (CplInfo&)>(&lookupBinaryTable));
  std::list<RuntimeObject> ob(rilist.size());
  std::transform(cilist.begin(), cilist.end(), rilist.begin(), ob.begin(),
		 std::ptr_fun(RuntimeObjectFactory));
  return ob;
}

Token& addFnConstructor(Token& t) {
  Token name(t);
  if(strncmp("p_void", t.c_str(), strlen("p_void")))
    {
      if(!strncmp(PTRTOKEN, t.c_str(), strlen(PTRTOKEN)))
	name = Token(t.begin() + strlen(PTRTOKEN), t.end());
      else if(!strncmp(ARRTOKEN, t.c_str(), strlen(ARRTOKEN)))
	name = Token(t.begin() + strlen(ARRTOKEN), t.end());
      else if(!strncmp(PARRTOKEN, t.c_str(), strlen(PARRTOKEN)))
	name = Token(t.begin() + strlen(PARRTOKEN), t.end());
    }
  t = name + CLASSDELM + t;
  return t;
}

CplInfo
parseXmlAttributes(const jncTypei, const std::string&);

CplInfo parseEach(const std::vector<Token>& pat) {
  std::vector<CplInfo> arg_list;
  std::vector<Token> fn_name(pat.begin(), --pat.end());
  ScanInput scan(getExpression(JCMP));   // Process fn args
  std::vector<Token> fn, args;
  Token s(*(++pat.begin()));
  if(scan(s.begin(), s.end(), fn))
    {
      CplInfo nextarg;
      if(scan(fn[1].begin(), fn[1].end(), args)) // keep reading argument
	nextarg = parseEach(fn); 
      else // process as new argument
	nextarg = parseXmlAttributes(JCMP, std::string(fn[0] + "(" + fn[1] + ")"));
      addFnConstructor(*fn_name.begin());
      fn_name.push_back(fn[0]); // save fn names
      arg_list.push_back(nextarg); // save fn type infos
    }
  CplInfo cinfo(lookupSymbolTable(fn_name));
  if(cinfo.getId() == UNSET)
    {
      if(pat[1].length()) // reconstruct fn for further processing
	cinfo.setSymbolicName(cinfo.getSymbolicName() + CLASSDELM + pat[1]);
    }
  else
    cinfo.setArgInfo(arg_list);
  return cinfo;
}

std::string removeWhiteSpace(const std::string& s) {
  typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer;
  boost::escaped_list_separator<char> sep('\\', ' ');
  std::string tmp;
  tokenizer tokens(s, sep);
  if(tokens.begin() != tokens.end())
    for(tokenizer::iterator i = tokens.begin(); i != tokens.end(); i++)
      tmp += *i;
  return (tmp.length())? tmp: s;
}

CplInfo
parseXmlAttributes(const jncTypei type, const std::string& kw) {
  
  Token tok(removeWhiteSpace(kw));
  std::vector<Token> fntokens;
  ScanInput scan(type);
  // for Array types, remove leading characters
  scan(tok.begin(), tok.end(), fntokens);
  if(!fntokens.size())
    {
      fntokens.resize(2);
      fntokens[0] = tok;
    }
  
  CplInfo tmp(parseEach(fntokens));
  return tmp;
}

RuntimeObject& evalNode(XmlTreeWalker* node, RuntimeObject& ob)
{
  
  if(!ob.oci && (ob.oci->getId() == UNSET) && !ob.ori) 
    return ob;
  FnInfo fn_param(getFnParameters(*ob.oci));
  if(!fn_param)
    return ob;
  std::vector<std::string> xmlargv;
  for(std::list<std::string>::iterator i = ++fn_param->pssignature->begin(); i != fn_param->pssignature->end(); i++)
    { // to get proper ordering of args
      std::pair<std::string, std::string> tmp(*i, std::string());
      xmlargv.insert(xmlargv.end(), node->getTagNode().attributes(tmp).second);
    }
  std::vector<jncTypei> type_param(xmlargv.size());
  std::copy(++fn_param->ptsignature->begin(), fn_param->ptsignature->end(), type_param.begin());
  std::vector<CplInfo> argci(xmlargv.size());
  for(int i = 0; i < xmlargv.size(); i++)
    argci[i] = parseXmlAttributes(type_param[i], xmlargv[i]);
  std::vector<RtmInfo> argri(argci.size());
  std::transform(argci.begin(), argci.end(), argri.begin(), std::ptr_fun(lookupBinaryTable));
  ob.oci->setArgInfo(argci);
  ob.ori->setArg(argri);
  return ob;
}

bool operator < (const RuntimeObject& x, const RuntimeObject& y)
{ 
  if(x.oci->getId() != UNSET)
    if(y.oci->getId() == UNSET)
      return true;
    else
      return (x.oci->getPriority() < y.oci->getPriority());
  else
    return false;
}

XmlTreeWalker* getNext(XmlTreeWalker* node)
{
  if(!node)
    return NULL;
  XmlPreOrder nextn(node);
  return (XmlTreeWalker*)&(nextn.next());
}

void Parse(std::list<JncContext>& cxt, XmlTreeWalker* node)
{
  std::list<RuntimeObject> ob(parseXmlElement(node));
  // Run Objects when possible or branch to another node
  for(std::list<RuntimeObject>::iterator i = ob.begin(); i != ob.end(); i++)
    if(i->oci->getId() != UNSET)
      switch(i->oci->getPriority()) {
      case NORMAL : // Run When seen
	evalNode(node, *i);
	cxt.insert(cxt.end(), JncContext(*i, node));
	break;
	case LOW : // run as last sibling
	  if((XmlTreeWalker*)&(XmlSiblingNode(node)+= (XmlSiblingNode(node).index() - (XmlSiblingNode(node).nbChildren() - 1))) != node)
	    Parse(cxt, getNext((XmlTreeWalker*)&(XmlSiblingNode(node)+= (XmlSiblingNode(node).index() - (XmlSiblingNode(node).nbChildren() - 1))))); 
	  evalNode(node, *i);
	  cxt.insert(cxt.end(), JncContext(*i, node));
	  break;
      case VLOW : // run child first
	if((XmlTreeWalker*)&XmlChildNode(node).next() != node)
	  Parse(cxt, getNext((XmlTreeWalker*)&XmlChildNode(node).next()));
	evalNode(node, *i);
	cxt.insert(cxt.end(), JncContext(*i, node));
	break;
      case XLOW : // Run as last sibling and child first
	if(((XmlTreeWalker*)&XmlChildNode(node).next() != node) || ((XmlTreeWalker*)&XmlSiblingNode(node).next() != node))
	  {
	    Parse(cxt, getNext((XmlTreeWalker*)&XmlChildNode(node).next()));
	    Parse(cxt, getNext((XmlTreeWalker*)&XmlSiblingNode(node).next()));
	  }
	evalNode(node, *i);
	cxt.insert(cxt.end(), JncContext(*i, node));
	break;
      }
}


